#include "api.h"
#include "http.h"
#include "url.h"
#include "properties.h"
#include "dbms.h"
#include "threadmanager.h"
#include "snprintf.h"
#include "leak.h"
#include "hash.h"
#include "smtp.h"
#include "malloc.h"

#ifdef LEAKCHECK
extern int tnum;
#define leakprint(n)    printf("caravan%d:%d\n",n,tnum)
#else
#define leakprint(n) 
#endif


#ifdef LOG
#define writeLog(s) writeLog(s)
#else
#define writeLog(s) 
#endif

char* versionString="4.78";
char* attached="_attach_";
char* redirected="_redirect_";
char* run_on_exit="_finish";

char* httpContentType="Content-type";

extern char* gateway;
/*
 * The number of status lines must equal the value of RESPONSE_CODES (http.h)
 * and must be listed in order.
 */

char* sheader="_header";

char* status_lines[] = {
   "100 Continue",
   "101 Switching Protocols",
#define LEVEL_200  2
   "200 OK",
   "201 Created",
   "202 Accepted",
   "203 Non-Authoritative Information",
   "204 No Content",
   "205 Reset Content",
   "206 Partial Content",
#define LEVEL_300  9
   "300 Multiple Choices",
   "301 Moved Permanently",
   "302 Moved Temporarily",
   "303 See Other",
   "304 Not Modified",
   "305 Use Proxy",
#define LEVEL_400 15
   "400 Bad Request",
   "401 Authorization Required",
   "402 Payment Required",
   "403 Forbidden",
   "404 File Not Found",
   "405 Method Not Allowed",
   "406 Not Acceptable",
   "407 Proxy Authentication Required",
   "408 Request Time-out",
   "409 Conflict",
   "410 Gone",
   "411 Length Required",
   "412 Precondition Failed",
   "413 Request Entity Too Large",
   "414 Request-URI Too Large",
   "415 Unsupported Media Type",
   "416 Requested range not available",

#define LEVEL_500 31
   "500 Internal Server Error",
   "501 Method Not Implemented",
   "502 Bad Gateway",
   "503 Service Temporarily Unavailable",
   "504 Gateway Time-out",
   "505 HTTP Version Not Supported",
   "506 Variant Also Varies",
   "507 UNKNOWN Error",
   "508 UNKNOWN Error",
   "509 UNKNOWN Error",
   "510 Not Extended"
#define RESPONSE_CODES 42
}; 

ULONG pid;   
char *closeConnection="Close";
const char* connectionKeepAlive()
{
	if(ThreadManager->getThreadSpace()==0)
		return closeConnection;
	return "Keep-Alive";
}

void getPid()
{ 
 int success;
 getProcessId(pid,success);
}

char* copyright="CARAVAN ENTERPRISE SERVER v%s/03D \nCopyright (c) 1999,2000,2001,2002 : Niti Telecom Consultancy\nAll rights are reserved. (http://www.nititelecom.com)\n";

/*    
HTTPServer::HTTPServer(USHORT port)
 : Server(port) 
{
 printf(copyright,versionString);
 printf("Recieve Mode\n");
 start ();
}
*/

HTTPServer::HTTPServer()
 : Server("caravan-http") 
{
 printf(copyright,versionString);
 start ();
}


HTTPClientHandler::HTTPClientHandler(int s)
: ClientHandler(s),persistent(0)
{
start();
}


char* getResultString(HTTPConstant status)
{
    static int shortcut[7] = {0, 0, LEVEL_200, LEVEL_300, LEVEL_400,
                               LEVEL_500, RESPONSE_CODES };
    int i, pos;

    if (status < 100)          // Below 100 is illegal for HTTP status 
        return status_lines[LEVEL_500];
    i = status/100;
    if(i < 7){
     pos = status%100 + shortcut[i];
     if( pos >= shortcut[i+1] )
      return status_lines[LEVEL_500];
     else
      return status_lines[pos];
    }
    return status_lines[LEVEL_500];
}

File *getHttpErrorFile(char *rc)
{
	HTTPConstant c=(HTTPConstant)503;
	if(rc){
		c=(HTTPConstant) atoi(rc);
	}
    File *f=new vFile("e404.html");
	f->write("<html><title>NetWork Error - Caravan Gateway Server</title><h1>");
	f->write(getResultString(c));
	f->write("</h1></html>");
	f->close();
	return f;
}

int requestno=0;
char location[32];
int getID(int);
void freeID(int,int);
int validSeed(char* s);

char tmpDirectory[128];
char etcDirectory[64];

#ifdef LOG
RealFile *logf=0;


void writeLog(char* s)
{
   Buffer *tmp=new Buffer(512);
   char *tmpstring=*tmp;
   if(logf==0){
      Time *t=new Time();
      logf=new RealFile(tmpDirectory,caravan.log);
      logf->setDataBase();
      if(logf->exists())
         logf->append("\n");
      else
         logf->create();
       logf->write(t->rfcFormat());
       logf->write("\n--------------------\n");
       delete t;
      }
   delete tmp;
   logf->write(s);
}

#endif

#define BUFFSIZE 8192

void relayHttp(Socket *client,Socket* server,HTTPRequest* rq,char *rqBuff)
{
	HttpResponse* h;
	do{
		int n=rq->getContentLength();
		delete rq;
		while(n>0){
			int l=n;
			if(l>BUFFSIZE)
				l=BUFFSIZE;
			l=client->Recv(rqBuff,l);
			if(l>0)
				n-=l;
			else
				break;
			int m=server->Send(rqBuff,l);
			if(m!=l)
				break;
		}
		if(n>0)
			break;
		char *r=rqBuff;
		for(n=0;n<BUFFSIZE-2;r++,n++){
			if(server->Recv(r,1)==1){
				if(*r=='\n' && n>2 && *(r-1)=='\r' && *(r-2)=='\n' && *(r-3)=='\r'){
					*(r+1)=0;
					n++;
					break;
				}
			}else{
				n=BUFFSIZE;
				break;
			}
		}
		h=0;
		if(n<BUFFSIZE){
			h=new HttpResponse(rqBuff,"res");
		}
		if(h!=0){
			if(client->Send(rqBuff,n)!=n){
				delete h;
				break;
			}
			n=h->getContentLength();
			delete h;
			if(n>=0){
				while(n>0){
					int l=n;
					if(l>BUFFSIZE)
						l=BUFFSIZE;
					l=server->Recv(rqBuff,l);
					if(l>0)
						n-=l;
					else
						break;
					int m=client->Send(rqBuff,l);
					if(m!=l)
						break;
				}
				if(n!=0)
					break;
			}else{
				while(1){
					int l=server->Recv(rqBuff,BUFFSIZE);
					if(l<=0)
						break;
					int m=client->Send(rqBuff,l);
					if(m!=l)
						break;
				}
				break;
			}
		}
		r = rqBuff;
		for(n=0;n<BUFFSIZE-2;r++,n++){
			if(client->Recv(r,1)==1){
				if(*r=='\n' && n > 2 && *(r-1)=='\r' && *(r-2)=='\n' && *(r-3)=='\r'){
					*(r+1)=0;
					n++;
					break;
				}
			}else{
				n=BUFFSIZE;
				break;
			}
		}
		if(n<BUFFSIZE){
			rq=new HTTPRequest(rqBuff);
			if(rq){
				if(server->Send(rqBuff,n)!=n){
					delete rq;
					break;
				}
			}else
				break;
		}else
			break;
	}while(1);
}




HTTPRequest* getHTTPRequestHeader(Socket *s)
{
	char requestBuff[BUFFSIZE];
	char *r = requestBuff;
	int n=0;
	for(;n<BUFFSIZE-2;r++,n++){
		if(s->Recv(r,1)==1){
			if(*r=='\n' && n > 2 && *(r-1)=='\r' && *(r-2)=='\n' && *(r-3)=='\r'){
				*(r+1)=0;
				n++;
				break;
			}
		}else{
			n=BUFFSIZE;
			break;
		}
	}
	if(n<BUFFSIZE){
		HTTPRequest *rq=new HTTPRequest(requestBuff);
		if(rq){
			Socket *srv=Connect(rq->showValue("host"));
			if(srv==0)
				return rq;
			if(srv->Send(requestBuff,n)==n){
				relayHttp(s,srv,rq,requestBuff);
			}else
				delete rq;
			delete srv;
		}
	}
	return 0;	
}



HttpResponse *getHttpResponse(Socket *s,char* objname)
{
	HttpResponse* h=0;
	char responseBuff[BUFFSIZE];
	char *r=responseBuff;
	int n;
	s->flush();
	for(n=0;n<BUFFSIZE-2;r++,n++){
		if(s->Recv(r,1)==1){
			if(*r=='\n' && n>2 && *(r-1)=='\r' && *(r-2)=='\n' && *(r-3)=='\r'){
				*(r+1)=0;
				break;
			}
		}else{
			n=BUFFSIZE;
			break;
		}
	}
	if(n<BUFFSIZE){
		h=new HttpResponse(responseBuff,objname);
		s->setContentLength(h->getContentLength());
	}
	return h;
}

/*
int  HTTPClientHandler::handleClient()
{
        int n;
        do{
                flush();
                SetTcpTimeout(90);
                char requestBuff[BUFFSIZE];
                char *r = requestBuff;
                for(n=0;n<BUFFSIZE-2;r++,n++){
                        if(Recv(r,1)==1){
                                if(*r=='\n' && n > 2 && *(r-1)=='\r' && *(r-2)=='\n' && *(r-3)=='\r'){
                                        *(r+1)=0;
                                        break;
                                }
                        }else{
                                n=BUFFSIZE;
                                break;
                        }
                }
                if(n<BUFFSIZE){
                        HTTPRequest* request=new HTTPRequest(requestBuff);
                        if(request->showValue(httpVersion)){
                                setContentLength(request->getContentLength());
                                persistent=request->keepAlive();
                                serviceHttpRequest(request);
                        }else{
                                delete request;
                                break;
                        }
                }else{
                        break;
                }
        }while(persistent && ThreadManager->getThreadSpace());
        return 0;
}
*/
mutex *mm=0;

void incServicing(int* x)
{
	if(mm==0)
		mm=new mutex();
	mm->lock();
	(*x)++;
	mm->release();
}

void decServicing(int* x)
{
	mm->lock();
	(*x)--;
	mm->release();
}

int  HTTPClientHandler::handleClient()
{		
	do{
		flush();
		SetTcpTimeout(90);
		HTTPRequest* request=getHTTPRequestHeader(this);
		if(request){
			if(request->showValue(httpVersion)){
				setContentLength(request->getContentLength());
				persistent=request->keepAlive();
				serviceHttpRequest(request);
			}else{
				delete request;
				break;
			}
		}else
			break;
	}while(persistent);
	return 0;
}



/*

int  HTTPClientHandler::handleClient()
{
   int n;
   Buffer *requestBuff=new Buffer(4096);
   char *r = *requestBuff;
   int rn=0;
   do{
     flush();
     if((n=Read(r,1024)) > 0){
      r[n]=0;
         }
     else
      break;
     }while(serviceHttpRequest(requestBuff,n) && keepAlive());
   delete requestBuff;
   return 0;
}

*/


URL* getURL(char* urlname);

ULONG startTime;

extern char* xauth;

Buffer* getXauth();
int getActiveThreads();

htmlObject* getGlobals(htmlObject* request)
{
        char rid[32];
        if(request==0){
                request=new htmlObject(new Buffer("request",8));
                request->putValue(xauth,getXauth());     
        }
        request->putValue("tmp",tmpDirectory);
        request->putValue("etc",etcDirectory);
        request->putValue("serverLocation",location);     
        request->putValue("domain",Domain);
        request->putValue("version",versionString);
        request->putValue("documents",(*serverConfig)["documents"]);
        request->putValue("httpPort",(*serverConfig)["caravan-http"]);
		char *s=(*serverConfig)["userdefined"];
		if (s && *s){
	        request->putValue("userdefined",(*serverConfig)["userdefined"]);
		}
        snprintf(rid,32,"%lu",startTime);
		request->putValue("TimeStart",rid);
		snprintf(rid,32,"%d",requestno);
		request->putValue("requestID",rid);
		snprintf(rid,32,"%d",getActiveThreads());
		request->putValue("Threads",rid);
        request->setReadOnly();
        return request;
}

int ClientHandler::handleClient()
{
        int biz=4096;
        Buffer *requestBuff=new Buffer(biz);
        char *r = *requestBuff;
        genericObject *rx=getGlobals();
        URL* u=getURL(getServiceName());
        rx->putValue("service",getServiceName());
        rx->putValue("event","init");
        u->open(this,rx);
        rx->putValue("event","null");
        int rptr=0;
        int timeout=90;
        SetTcpTimeout(timeout);
        do{

                flush();
                char *s=rx->showValue("rcvTimeout");
                int t=timeout;
                if(s && *s){
                        t=atoi(s);
                        if(t<=0)
                                t=timeout;
                }
                if(t!=timeout)
                        SetTcpTimeout(timeout);
                int rcvCount=0;
                htmlBuffer* eom=rx->getValue("eom");
                s=rx->showValue("rcvCount");
                if(s&&*s){
                        rcvCount=atoi(s);
                        if(rcvCount<0)
                                rcvCount=0;
                        if(rcvCount+1>biz){
                                Buffer *tmp=requestBuff;
                                requestBuff=new Buffer(biz=rcvCount+1);
                                r=*requestBuff;
                                if(rptr>0)
                                        memcpy(r,*tmp,rptr);
                                delete tmp;
                        }
                }
                int n=rptr;
                int complete=(rptr>=rcvCount);
                while((rptr==0 && s==0) || (complete==0)){
                        complete=0;
                        if(eom){
                                n=Recv(r+rptr,1);
                        }else{
                                n=Read(r+rptr,biz-rptr-1);
                        }
                        if(n<=0){
                                rx->putValue("close","Recv Error");
                                deleteHtmlBuffer(eom);
                                break;
                        }
                        rptr+=n;
                        if(eom==0){
                                complete=(rptr>=rcvCount);
                        }else{
                                htmlBuffer *prev=0;
                                for(htmlBuffer *z=eom;z!=0;z=z->next){
                                        int eomlen=z->getSize()-1;
                                        if(rptr>eomlen){
                                                char *e=*z;
                                                if(memcmp(r+rptr-eomlen,e,eomlen)==0){
                                                        complete=1;
                                                        if(prev){
                                                                prev->next=0;
                                                                deleteHtmlBuffer(eom);
                                                        }
                                                        deleteHtmlBuffer(z->next);
                                                        z->next=0;
                                                        rx->putValue("eomHit",z);
                                                        break;
                                                }
                                        }
                                        prev=z;
                                }
                        }
                        if(complete==0){
                                if(rcvCount>0 && rptr>=rcvCount)
                                        complete=1;
                                else{
                                        if(rptr==requestBuff->getSize()){
                                                Buffer *tmp=new Buffer(rptr+1024);
                                                memcpy(*tmp,r,rptr);
                                                delete requestBuff;
                                                requestBuff=tmp;
                                                r=*requestBuff;
                                        }
                                }
                        }else
                                break;
                }
                if(rptr > 0){
                        if(n<=0){
                                rx->putValue("close","Recv Error");
                        }
                        r[rptr]=0;
                        Buffer *b=0;
                        if(rcvCount>0){
                                if(rptr>rcvCount){
                                        b=new Buffer(r,rcvCount+1);
                                        s=*b;
                                        s[rcvCount]=0;
                                        rptr-=rcvCount;
                                        memcpy(r,r+rcvCount,rptr);
                                }else{
                                        b=new Buffer(r,rptr+1);
                                        rptr=0;
                                }
                        }else{
                                b=new Buffer(r,rptr+1);
                                rptr=0;
                        }
                        rx->putValue("event","dataReady");
                        rx->putValue("msg",new htmlBuffer(b));
						snprintf(r+rptr,12,"%d",b->getSize()-1);
						rx->putValue("msgSize",r+rptr);
                        u->open(this,rx);
                        rx->putValue("event","null");
                        htmlBuffer *s=rx->getValue("send");
                        if(s!=0){
                                htmlBuffer *b=s;
                                while(s){
									int n=s->getSize()-1;
									if(n>0){
										if(Send(*s,n)<0){
											rx->putValue("close","Send Error");
											break;
										}
									}
									s=s->next;
                                }
                                deleteHtmlBuffer(b);
                                rx->putValue("send","null");
                        }
                        dispose(rx->next);
                        rx->next=0;
						if(rx->showValue("msg")==0){
							rx->putValue("msgsize","null");
							rx->putValue("eomHit","null");
						}
                        if(rx->showValue("close"))
                                break;
                }
                else
                        break;
        }while(1);
        rx->putValue("event","close");
        u->open(this,rx);
        dispose(rx);
        delete requestBuff;
        return 0;
}

void getStartupTime(char* s)
{
        Time* t=new Time(startTime);
        strcpy(s,t->local());
        delete t;
}

int httpg=0;
int httpp=0;

void  HTTPClientHandler::serviceHttpRequest(HTTPRequest *request)
{
	char rid[40];
	register int i=0;
	EnterCritSec();
	int rqn=++requestno;
	ExitCritSec();
	int id=-1;
	leakprint(1);
	leakprint(2);
    HTTPConstant hc=request->getError();
	if(hc!=HTTP_OK){
		sendHttpError(hc);
		delete request->next;
		delete request;
		return;
	}
    char* requestURL=request->showValue(URI);
	if(strnicmp(requestURL,"http://",7)==0 || strnicmp(requestURL,"ftp://",6)==0){
		request->putValue(httpProxy,"1");
	}
    writeLog("URL: ");
    writeLog(requestURL);
    writeLog("\n");
    writeLog("clientAddr: ");
    writeLog(getPeerAddress());
    writeLog("\n");
	getGlobals(request);
    request->putValue("serveraddress",getMyAddress());
    request->putValue("address",getPeerAddress());
	snprintf(rid,32,"%lu",getTime());
    request->putValue("request_time",rid);
    request->setReadOnly();
    genericObject *cookie=request->next;
    char *seed=0;
    if(cookie){
		seed=cookie->showValue(cookieName);
		if(validSeed(seed)==0){
			delete cookie;
			cookie=0;
			request->next=0;
			seed=0;

/*
            cookie->setReadWrite();
			cookie->putValue(cookieName,"null");
			seed=0;
			cookie->setReadOnly();
*/
		}
	}
    if(seed==0){
		id=getID(rqn);
		if(id==-1){
			sendHttpError(HTTP_UNAVAILABLE);
			delete request;
			return;
		}
		if(cookie==0){
			cookie = new htmlObject(new Buffer("cookie",7));
			request->next=cookie;
		}
		snprintf(rid,40,"%d-%ld-%u",id,getTime()%100000L,rqn);
		writeLog("id: ");
		writeLog(rid);
		writeLog("\n");
		cookie->putValue(cookieName,rid);
	}else{
		mkObjectList(cookie);
		writeLog("id: ");
		writeLog(cookie->showValue(cookieName));
		writeLog("\n");
	}
	writeLog("requestType:");
	HTTPMethod m=request->getMethod();
	switch(m){
	case MHEAD:
	case MGET:
	case HEAD:
	case GET:
	case MPUT:
	case PUT:
		leakprint(21);
		incServicing(&httpg);
		i=serviceGET(request);
		decServicing(&httpg);
		writeLog("get\n");     
		leakprint(22);
		break;
	case MPOST:
	case POST:
		incServicing(&httpp);
		i=servicePOST(request);
		decServicing(&httpp);
		writeLog("post\n");
		break;
	default : i=0;
		break;
	}
	persistent&=i;
    if((i==0) && (id!=-1))
		freeID(id,rqn);
	leakprint(3);
    deleteObjs(request);
	leakprint(4);
    writeLog("........................................\n");
}



void decodeUrl(char*s);

void cleanUP(genericObject *r,char* requestUrl)
{
 for(genericObject* objs=r->next,*p=r;objs!=0;objs=objs->next){
     char *s;
     if((s=objs->getCaller())!=0){
        if(stricmp(requestUrl,s)){
           p->next=objs->next;
           delete objs;
           objs=p;
           }
        }
      p=objs;
     }
}


extern char* httpIfModified;
char* ifunmod="if-unmodified-since";
char* ifmod="if-modified-since";
char* ifmatch="if-match";
char* ifnonematch="if-none-match";
char* ifrange="if-range";
char* range="range";
#define RANGE 0
#define IFRANGE 1
#define IFMATCH 2
#define IFNONMATCH 3
#define IFMOD 4
#define IFUNMOD 5

htmlBuffer* checkRange(char* range,int z)
{
	miner *m=new miner();
	char tmp[32];
	int cl=0;
	htmlBuffer *x=0;
	htmlBuffer *l=0;
	if(m->mine(range,"~%bytes~%=~%`*`")){
		Buffer *ranges=m->getValueFinal(0);
		range=*ranges;
		for(char *s=range;;range++){
			if(*range==',' || *range==0){
				char c=*range;
				*range=0;
				if(m->mine(s,"~%`~#`~%-~%`~#`~%")){
					char *bs=m->getValue(0);
					char* es=m->getValue(1);
					int e=atoi(es);
					int b=atoi(bs);
					int n=0;
					if(e>=0 && b>=0){
						if(*es && *bs){
							if(b<=e){
								if(e>=z)
									e=z-1;
								n=e-b+1;
							}else{
								b=n=z;//error condition;
							}
						}else{
							if(e>0 || b>0){
								if(e){
									n=e;
									if(n>z){
										e=z-1;
										n=z;
									}
									b=z-n;
								}else{
									e=z-1;
									n=z-b;
								}
							}
						}
					}
					if(n>0){
						cl=snprintf(tmp,256,"%d/%d",b,n);
						htmlBuffer* t=new htmlBuffer(new Buffer(tmp,cl+1));
						if(x==0)
							x=t;
						else
							l->next=t;
						l=t;
					}
				}
				if(c==0)
					break;
				*range=',';
				s=range+1;
			}
		}
		delete ranges;
	}
	delete m;
	return x;
}

extern  int wordhash(char*);

HTTPConstant HTTPClientHandler::checkRequestConditions(char** conditions,HTTPRequest * r,genericObject *response,File *of)
{
	HTTPConstant result=HTTP_OK;
	char *cond;
	char *etag=response->showValue(httpETag);
	char *lastmodified=response->showValue("last-modified");
	ULONG lastmodifiedTime=0;
	if(lastmodified)
		lastmodifiedTime=convertRFCformat(lastmodified);
	if(lastmodifiedTime==0)
		lastmodifiedTime=of->lastWriteTime();
	HTTPMethod m=r->getMethod();
	if (conditions[RANGE]){
		r->putValue("x-ranges","null");
		htmlBuffer *ranges;
		int cl=of->size();
		if((ranges=checkRange(conditions[RANGE],cl))!=0){
			if((ranges->next==0) && (atoi(*ranges)<cl)){
				r->putValue(new Buffer("x-ranges",9),ranges);
				result=HTTP_PARTIAL_CONTENT;
				if((cond=conditions[IFRANGE])!=0){
					ULONG t=convertRFCformat(cond);
					if(t==0){
						if(strcmp(cond,"*") && strcmp(cond,etag)){
							if(m==GET)
								result=HTTP_OK;
							else
								result=HTTP_PRECON_FAILED;
						}
					}else{
						if(lastmodifiedTime>t){
							if(m==GET)
								result=HTTP_OK;
							else
								result=HTTP_PRECON_FAILED;
						}
					}
					return result;
				}
			}else{
				deleteHtmlBuffer(ranges);
				return HTTP_RANGE_NOT_SATISFIABLE;
			}
		}else{
			conditions[RANGE]=0;
		}
	}
	cond=conditions[IFMATCH];
	if(cond){
		int ok=0;
		for(char* s=cond;;++s){
			if(*s==',' || *s==0){
				char c=*s;
				*s=0;
				if(strcmp(cond,"*")==0 || strcmp(etag,cond)==0){
					*s=c;
					ok=1;
					break;
				}
				if(c==0)
					break;
				*s=',';
				cond=s+1;
			}
		}
		if(ok==0)
			result=HTTP_PRECON_FAILED;
		return result;
	}else{
		cond=conditions[IFNONMATCH];
		if(cond){
			for(char* s=cond;;++s){
				if(*s==',' || *s==0){
					char c=*s;
					*s=0;
					if(strcmp(cond,"*")==0 || strcmp(etag,cond)==0){
						result=HTTP_NOT_MODIFIED;
						break;
					}
					if(c==0)
						break;
					*s=',';
					cond=s+1;
				}
			}
			return result;
		}
	}
	cond=conditions[IFUNMOD];
	if(cond){
		ULONG t=convertRFCformat(cond);
		if(t>0 && lastmodifiedTime>t){
			result=HTTP_PRECON_FAILED;
		}
	}
	else{
		cond=conditions[IFMOD];
		if(cond){
			ULONG t=convertRFCformat(cond);
			if(t>0 && lastmodifiedTime<=t){
				result=HTTP_NOT_MODIFIED;
			}
		}
	}
    return result;
}

int httpi=0;
int httpj=0;

int ignoreConditions=0;

int HTTPClientHandler::sendHttpResponse(HTTPRequest * r,URL *url)
{
	int success=0;
	File* of=url->open(this,r);
	if(of==0){
		return sendHttpError(HTTP_NOT_FOUND);
	}
	incServicing(&httpi);
	genericObject *response=getObject(sRESPONSE,r);
	genericObject *tr=0;
	char *rc=0;
	HTTPConstant result_code=HTTP_OK;
	htmlBuffer *attach=0;
	if(response==0){
		response=new htmlObject();
		tr=response;
	}else{
		rc=response->showValue(redirected);
		if(rc){
			if(of->isReal()==RVFILE){
				of->close();
				of->deleteFile();
			}
			delete of;
			decServicing(&httpi);
			return httpRedirect(rc);
		}
		response->setReadOnly();
		rc=response->showValue(responseCode);
		if(rc && *rc)
			result_code=(HTTPConstant) atoi(rc);
		attach=response->getValue(attached);
	}
	char* conditions[8];
	memset(conditions,sizeof(conditions),0);
	if(r->showValue("ignore-range")==0)
		conditions[RANGE]=r->showValue(range);
	conditions[IFRANGE]=r->showValue(ifrange);
	conditions[IFMATCH]=r->showValue(ifmatch);
	conditions[IFNONMATCH]=r->showValue(ifnonematch);
	conditions[IFMOD]=r->showValue(ifmod);
	conditions[IFUNMOD]=r->showValue(ifunmod);
	int attSize=0;
	File *fileSent=of;
	char tmp[256];
	if(attach!=0){
		fileSent=0;
		attSize=attach->getSize()-1;
		File* fd;
		if((fd=attach->getFile())!=0){
			if(of->size()==0){
				if(response->showValue(httpContentType)==0)
					response->putValue(httpContentType,fd->getMimeType());
				if(attSize>0){
					fileSent=fd;
				}
			}
		}
	}
	if(fileSent!=0){
		if(fileSent->size()>0){
			char *requestUrl=r->showValue(URI);
			char *etag=response->showValue(httpETag);
			if((etag==0) && (requestUrl!=0)){
				snprintf(tmp,64,"\"f%xz%xx%x\"",wordhash(requestUrl),fileSent->size(),fileSent->getCrc());
				response->putValue(httpETag,tmp);
			}
			if((rc==0) && (ignoreConditions==0))
				result_code=checkRequestConditions(conditions,r,response,fileSent);
		}
	}
	decServicing(&httpi);
	incServicing(&httpj);
	URLhandler *u = getURLhandler(r->showValue(run_on_exit));
	if(u==0){
			r->putValue(run_on_exit,"null");
	}
	if(result_code/100!=2){
		if(u)
			success=sendHttpErrorLog(result_code,r);
		else
			success=sendHttpError(result_code);
	}else{
		int beginFrom=0;
		int rangeSize=0;
		if((rc==0) && (result_code==HTTP_PARTIAL_CONTENT)){
			char* v=r->showValue("x-ranges");
			if(v && *v){
				for(int i=0;v[i]!=0;++i){
					if(v[i]=='/'){
						beginFrom=atoi(v);
						rangeSize=atoi(v+i+1);
						break;
					}
				}
			}
		}
		if(rangeSize>0){
			snprintf(tmp,64,"bytes %d-%d/%d",beginFrom,beginFrom+rangeSize-1,fileSent->size());
			response->putValue("Content-Range",tmp);
			success=sendHttpResponseHeader(fileSent,result_code,r,response,rangeSize,url->dynamic());
			if((r->getMethod()!=HEAD) && success)
				success=(sendHttpContent(fileSent,this,0,0,beginFrom,rangeSize)>0);
		}
		else{
			if(of->size()>0){
				success=sendHttpResponseHeader(of,result_code,r,response,of->size()+attSize,url->dynamic());
				if((r->getMethod()!=HEAD) && success)
					success=(sendHttpContent(of,this)>0);
			}else{
				success=sendHttpResponseHeader(fileSent,result_code,r,response,attSize,url->dynamic());
			}
			if(attSize>0 && (r->getMethod()!=HEAD) && success>0){
				File *fd=attach->getFile();
				if(fd){
					success=(sendHttpContent(fd,this)>0);
				}else{
					success=(Send(*attach,attSize)>0);
				}
			}
		}
	}
	if(of->isReal()==RVFILE){
		of->close();
		of->deleteFile();
	}
	delete of;
	delete tr;
	delete attach;
	if(u!=0){
		if(success)
			r->putValue("result","ok");
		else
			r->putValue("result","Transfer Failed");
		u->open(0,r,Console);
	}
	decServicing(&httpj);
	return success;
}


File* savePartialContent(File *f,HTTPRequest *r)
{
	if(f!=0 && f->size()>0){
		URLhandler *u = getURLhandler("http_partial_content.html");
		if (u!=0){
			char* tmv=r->showValue("last-modified");
			if (tmv!=0){
				f->setLastWriteTime(convertRFCformat(tmv));
			}								
			htmlBuffer *tmpf=new htmlBuffer(f);
			r->putValue(new Buffer(httpContent,strlen(httpContent)+1),tmpf);
			u->open(0,r,Console);
			f=0;
		}
	}
	if(f!=0)
		f->deleteFile();
	return f;
}


int httpr=0;
int httpc=0;


int HTTPClientHandler::serviceGET(HTTPRequest *r)
{
	int success=1;
	char *requestUrl=r->showValue(URI);
	if(requestUrl==0 || *requestUrl==0)
		return 0;
	HTTPMethod method = r->getMethod();
	miner *m=new miner();
	htmlObject *form=0;
	Buffer *b;
	if(m->mine(requestUrl,"/`*`\\?`*`")){
		b=m->getValueFinal(0);
		form=new htmlObject(new Buffer("form",5));
		form->readForm(m->getValueFinal(1),m);
	}else
		b=new Buffer(requestUrl+1,strlen(requestUrl+1)+1);
	requestUrl=*b;
	delete m;
	decodeUrl(requestUrl);
	cleanUP(r,requestUrl);
	r->putValue("url",requestUrl);
	URL* url=0;
	if(method==PUT){
		char *puturi=(*serverConfig)["put"];
		if(puturi && *puturi){
			url = getURL(puturi);
			if(url){
				File *f=getHttpContent(r,requestUrl,this,r->getContentLength(),&success);
				if((success==1) && (f!=0)){
					char* tmv=r->showValue("last-modified");
					if (tmv!=0){
						f->setLastWriteTime(convertRFCformat(tmv));
					}
					r->putValue(new Buffer(httpContent,strlen(httpContent)+1),new htmlBuffer(f));   
					r->putValue(new Buffer("content",8),r->getValue(httpContent));  
				}else{
					if(success==HTTP_RESUME){
						success=sendHttpError(HTTP_NOT_ACCEPTABLE);
					}else{
						f=savePartialContent(f,r);
					}
					delete f;
					delete b;
					delete form;
					return success;
				}
			}
		}
	}else
		url = getURL(requestUrl);
	delete b;
	if(url==0){
		delete form;
		success=sendHttpError(HTTP_NOT_FOUND);
		incServicing(&httpc);
		getHttpContent(r,0,this,r->getContentLength(),&success);
		decServicing(&httpc);
		return success;
	}
	requestUrl = url->getName();
	if(form){
		form->next=r->next;
		form->setReadOnly();
		r->next=form;
	}
	incServicing(&httpr);
	success=sendHttpResponse(r,url);
	decServicing(&httpr);
	if(url->getUrlType()==FILED){
		delete url;
	}
	return(success);
}


int HTTPClientHandler::servicePOST(HTTPRequest *r)
{
	char *requestUrl=r->showValue(URI);
	if(requestUrl==0 || *requestUrl==0)
		return 0;
	char *contentType = r->showValue(httpContentType);
	miner *m=new miner();
	htmlObject *form=0;
	Buffer *b;
	if(m->mine(requestUrl,"/`*`\\?`*`")){
		b=m->getValueFinal(0);
		form=new htmlObject(new Buffer("form",5));
		form->readForm(m->getValueFinal(1),m);
	}else
		b=new Buffer(requestUrl+1,strlen(requestUrl+1)+1);
	decodeUrl(requestUrl);
	cleanUP(r,requestUrl);
	r->putValue("url",requestUrl);
	if((*requestUrl!='/') || (contentType==0)){
		contentType="unknown/unknown";
	}
	requestUrl=*b;
	URL *url = getURL(requestUrl);
	delete b;
	int success=1;
	if(url && url->getUrlType()==PIPED){
		File *f=getHttpContent(r,"body",this,r->getContentLength(),&success);
		if((success==1) && (f!=0)){
			f->close();
			if(m->mine(contentType,"multipart/form-data*;~%boundary~%=~%~\"`*`~\"")){
				char *border=m->getValue(0);
				if(form==0)
					form = new htmlObject(new Buffer("form",5));
				fileStream *fs=new fileStream(f); 
				inputStream *in=new inputStream(fs);
				form->readForm(in,border);
				delete in;
				delete fs;
			}else{
				if(strnicmp(contentType,"application/x-www-form-urlencoded",33)==0){
					int n=f->size();
					if(n<1024*1024){
						if(form==0)
							form = new htmlObject(new Buffer("form",5));
						Buffer *buf=new Buffer(n+1);
						char *b=*buf;
						f->read(b,n);
						b[n]=0;
						form->readForm(buf,m);
					}
				}
			}
			f->close();
			htmlBuffer *tmpf=new htmlBuffer(f);
			r->putValue(new Buffer(httpContent,strlen(httpContent)+1),tmpf);
		}else{
			delete form;
			delete m;
			if(success==HTTP_RESUME){
				success=sendHttpError(HTTP_NOT_ACCEPTABLE);
			}else{
				f=savePartialContent(f,r);
			}
			delete f;
			return success;
		}
	}else{
		delete form;
		delete m;
		success=sendHttpError(HTTP_NOT_FOUND);
		incServicing(&httpc);
		getHttpContent(r,0,this,r->getContentLength(),&success);
		decServicing(&httpc);
		return success;
	}
	if(form){
		form->setReadOnly();
		form->next=r->next;
		r->next=form;
	}
	incServicing(&httpr);
	success=sendHttpResponse(r,url);
	decServicing(&httpr);
	if(url->getUrlType()==FILED)
		delete url;
	delete m;
	return success;
}

inline int HTTPClientHandler::httpRedirect(char *url)
{
	const char *keepAliveStr;
	if(persistent==0 || strnicmp(url,"http://",7)==0)
		keepAliveStr=closeConnection;
	else
		keepAliveStr=connectionKeepAlive();
	char header[2048];
	int n=snprintf(header,2048,"HTTP/1.1 %s\r\nLocation: %s\r\nConnection: %s\r\n",getResultString(HTTP_MOVED_TEMP),url,keepAliveStr);
	persistent=(keepAliveStr!=closeConnection);
	if(Send(header,n)>0){
		int k=snprintf(header,2048,"Moved <a href=\"%s\">here</a>\r\n",url);
		n=snprintf(header+k,2048-k,"Content-length: %d\r\n\r\n",k);
		if(Send(header+k,n)>0){
			return(Send(header,k)>0);
		}
    }
	return 0;
}

#define MAXHEADER 4095

int HTTPClientHandler::sendHttpResponseHeader(File * of,HTTPConstant result,HTTPRequest *r,genericObject *response,ULONG fsize,int dyn)
{
	int contenttype=0;
	int cachecontrol=0;
	int lastmodified=0;
	int expires=0;
	int server=0;
	int scook=0;
	int ranges=0;
	int date=0;
	char* hV=0;
	Buffer* hbuff=new Buffer(MAXHEADER+1);
	char* header=*hbuff;
	if(response){
		char *rc=response->showValue(responseCode);
		if(rc){
			HTTPConstant rn = (HTTPConstant) atoi(rc);
			rc=getResultString(rn);
			result=(HTTPConstant)atoi(rc);
		}
		hV=response->showValue(httpVersion);
	}
	char *httpVersion="1.1";
	if(hV)
		httpVersion=hV;
	int cl=snprintf(header,MAXHEADER,"HTTP/%s %s\r\n",httpVersion,getResultString(result));
	int proxy=(r->showValue(httpProxy)!=0);
	char *tcpParam=r->showValue(tcpBufferSize);
	if(tcpParam && *tcpParam){
		SetTcpBufferSize(atoi(tcpParam));
	}
	if(response){
		char *s;
		for(int i=0;(s=response->getPropertyName(i))!=0;++i){
			if(*s!='_'){
				char* v=response->getValue(i,0);
				if(v){
					int etags=0;
					if(stricmp(s,"server")==0)
						server=1;
					else
						if(stricmp(s,httpConnection)==0){
							persistent=(stricmp(v,"close")!=0);
							continue;
						}else
							if(stricmp(s,httpContentType)==0)
								contenttype=1;
							else
								if(stricmp(s,"cache-control")==0)
									cachecontrol=1;
								else
									if(stricmp(s,"last-modified")==0)
										lastmodified=1;
									else
										if(stricmp(s,"expires")==0)
											expires=1;
                                        else
											if(stricmp(s,httpETag)==0){
												etags=1;
											}
											else
												if(stricmp(s,"date")==0)
													date=1;
												else
													if(stricmp(s,"set-cookie")==0){
														scook=1;
													}
													else
														if(stricmp(s,"accept-ranges")==0){
															ranges=1;
														}
														else
															if(stricmp(s,"content-length")==0){
																continue;
															}
															if(*v!=0){
																for(int j=0;(v=response->getValue(i,j))!=0;++j){
																	char *hs=v;
																	while(*hs>=' ')
																		hs++;
																	*hs=0;
																	char* q="%s: %s\r\n";
																	if(etags){
																		s=httpETag;
																		if(*v!='"'){
																			q="%s: \"%s\"\r\n";
																		}
																	}
																	cl+=snprintf(header+cl,MAXHEADER-cl,q,s,v);                                                }
															}
				}
			}
		}
	}
	const char* keepAliveStr;
	if(proxy==0){
		if(date==0){
			cl+=snprintf(header+cl,MAXHEADER-cl,"Date: %s\r\n",rfcTime(header+256));
		}
		if(persistent==0)
			keepAliveStr=closeConnection;
		else
			keepAliveStr=connectionKeepAlive();
		cl+=snprintf(header+cl,MAXHEADER-cl,"Connection: %s\r\n",keepAliveStr);
		if(fsize>0){
			if(server==0){
				cl+=snprintf(header+cl,MAXHEADER-cl,"Server: CARAVAN %s\r\n",versionString);
			}
			if(ranges==0){
				cl+=snprintf(header+cl,MAXHEADER-cl,"Accept-ranges: bytes\r\n");
			}
			if((result>=(HTTPConstant) 200) && (result<=(HTTPConstant) 206)){
				if(of){
					if(contenttype==0)
						cl+=snprintf(header+cl,MAXHEADER-cl,"%s: %s\r\n",httpContentType,of->getMimeType());
					if(lastmodified==0){
						cl+=snprintf(header+cl,MAXHEADER-cl,"Last-Modified: %s\r\n",rfcTime(header+MAXHEADER-128,of->lastWriteTime()));
					}
					int expiry=0;
					char* expiresHeader="Expires: ";
					if(cachecontrol==0){
						if(dyn){
							char *cacheCntrl="Cache-Control: max-age=0,must-revalidate\r\n";
							if(getObject(sUSERINFO,r)){							
								cacheCntrl="Cache-Control: private,max-age=0,must-revalidate\r\n";
								expiresHeader="Pragma: no-cache\r\nExpires: ";
							}
							strcpy(header+cl,cacheCntrl);
							cl+=strlen(header+cl);
						}else{
							expiry=3600*240;
							cl+=snprintf(header+cl,MAXHEADER-cl,"Cache-Control: max-age=%d,must-revalidate\r\n",expiry);
						}
					}
					if(expires==0){
						rfcTime(header+MAXHEADER-128,getTime()+expiry);
						cl+=snprintf(header+cl,MAXHEADER-cl,"%s%s\r\n",expiresHeader,header+MAXHEADER-128);
					}
				}							
			}
			genericObject *cookie=getObject("cookie",r);
			if(cookie){
				strcpy(header+cl,"Set-Cookie: ");
				int l=12;
				char *s;
				int i=0;
				for(;(s=cookie->getPropertyName(i))!=0;++i)
					l+=snprintf(header+cl+l,MAXHEADER-cl-l,"%s=%s;",s,cookie->getValue(i,0));
				if(i>0){
					cl+=l;
					strcpy(header+cl,"\r\n");
					cl+=2;
				}
			}
		}
	}else{
		char *v=r->showValue(proxyConnection);
		if(v)
			persistent=(stricmp(v,"close")!=0);
		if(persistent==0)
			keepAliveStr=closeConnection;
		else
			keepAliveStr=connectionKeepAlive();
		cl+=snprintf(header+cl,MAXHEADER,"%s: %s\r\n",proxyConnection,keepAliveStr);
	}
	cl+=snprintf(header+cl,MAXHEADER,"Content-length: %ld\r\n",fsize);
	strcpy(header+cl,"\r\n");
	cl+=2;
	if(r->showValue(run_on_exit)){
		Buffer *tmp=new Buffer(header,cl+1);
		r->putValue(sheader,tmp);
	}
	int sn=Send(header,cl);
	delete hbuff;
	persistent=(keepAliveStr!=closeConnection);
	return (sn==cl);
}




int HTTPClientHandler::sendHttpError(HTTPConstant m)
{
   char error[640];
   const char* keepAliveStr;
   if(persistent==0)
	   keepAliveStr=closeConnection;
   else
	   keepAliveStr=connectionKeepAlive();
   int l=snprintf(error,512,"HTTP/1.1 %s\r\nDate: %s\r\nConnection: %s\r\nContent-Length: 0\r\n\r\n",getResultString(m),rfcTime(error+512),keepAliveStr);
   persistent=(keepAliveStr!=closeConnection);
   return Send(error,l)>0;
}

int HTTPClientHandler::sendHttpErrorLog(HTTPConstant m,HTTPRequest* r)
{
	char error[640];
   	const char* keepAliveStr;
	if(persistent==0)
		keepAliveStr=closeConnection;
	else
		keepAliveStr=connectionKeepAlive();
	int l=snprintf(error,512,"HTTP/1.1 %s\r\nDate: %s\r\nConnection: %s\r\nContent-Length: 0\r\n\r\n",getResultString(m),rfcTime(error+512),keepAliveStr);
	r->putValue(sheader,error);
	persistent=(keepAliveStr!=closeConnection);
	return Send(error,l)>0;
}


void initCodeMem();
void initDynamo();
void initCaravan();
void startScheduler();


mutex* objectLock;
mutex* mailmx;

void testLzw();

char *getExportName()
{
   return "caravan";
}


Buffer *findaWord(char*,char*);
/*
void createLogo()
{
   File *fp=new File("logo.gif");
   RealFile *fi=new RealFile("logo.cpp");
   if(fi->exists())
      fi->replace();
   else
      fi->create();
   char wb[8];
   int t=fp->size();
   unsigned char c;
   int l=0;
   for(int i=0;i<t;++i){
      fp->read((char*)&c,1);
      snprintf(wb,8,"%d,",c);
      l+=fi->write(wb);
      if(l>80){
         l=0;
         fi->write("\n");
         }
      }
   delete fp;
   delete fi;
}
*/


char dbHome[128];
char Domain[64];
void initBase64();
char templatePath[128];

Server* startFtp();

void gethostinfo(char*);
void init_bf();
void initOpenSocks();
void testFinder();

int MAXIDLETIME;
#include <fcntl.h>


HTTPServer *httpd = 0;
SMTPServer *smtpd=0;
Server * ftpd=0;
Server * generic[16];

int checkDirectory(char* cf)
{
        int rc=0;
        if(cf && *cf){
                RealFile *fd=new RealFile(cf,"check123");       
                if(fd->exists()){
                        fd->deleteFile();
                }
                fd->create();
                fd->write("caravan");
                fd->close();
                rc=fd->size();
                fd->deleteFile();
                delete fd;
        }
        return rc==7;
}

extern char progpath[];
int repair=0;
int repairDone=0;

char* getService(char* servicename)
{
        return (*serverConfig)[servicename];
}

char* getServerAddress()
{
return (*serverConfig)["address"];
}

int closeFiles();
void flushSeeker();
extern int quitCar;
void setCacheSize(int n);


void shutDown()
{
	quitCar=1;
	SleepMilSec(2000);
	printf("Shutting down Caravan ..");
	while(1){
		printf(".");
		EnterMustComplete();
		if(closeFiles()==0){
			flushSeeker();
			ExitMustComplete();
			break;
		}else
			ExitMustComplete();
		SleepMilSec(1000);
	}
	printf("done\r\n");
	SleepMilSec(200);
	EnterCritSec();
	delete httpd;
	httpd=0;
	delete smtpd;
	smtpd=0;
	delete ftpd;
	ftpd=0;
	for(int k=0;generic[k]!=0;k++)
		delete generic[k];
	ExitCritSec();
	SleepMilSec(3000);
}

Pipe *console=0;

void PrintConsole(char* string,int len)
{
#ifdef GUI
 if(console)
      console->write(string,len);
#else
     ::write(STDOUT_FILENO,string,len);
#endif
}

void initQmx();
void initVlock();

void initialize()
{
	initApi();
	SetPriorityServer();
	initBase64();
	getPid();
	objectLock=new mutex();
	initQmx();
	initVlock();
	mailmx=new mutex();
	initTimeZone();
	requestno=startTime%1024;
	initFileSystem();
	CreateThreadManager();
}
  


int carmain()
{
	#ifdef TARGETLINUX
	 void catch_int(int);
	 signal(SIGINT, catch_int); 
	 signal(SIGTERM, catch_int);
	#endif 
	char line[80];
	initChars();
	initProperties();
	initOpenSocks();

	char *biz=(*serverConfig)["filebuffer"];
	if(biz){
		int n=atoi(biz);
		if (n>0 && n <= 2048){
			BFSIZE=1024*n;
		}
	}
	biz=(*serverConfig)["tcpwindowsize"];
	if(biz){
		int n=atoi(biz);
		if (n>0 && n <= 2048){
			tcpwindowsize=1024*n;
		}
	}
	printf("FileBuffer=%d ; tcpwindowsize=%d\r\n",BFSIZE,tcpwindowsize);
	startTime=getTime();
	char *scfg=(*serverConfig)["dbhome"];
	if(*scfg==0){
		printf("Please go to the 'Settings' menu to install caravan\n");
		return 0;
	}
	MAXIDLETIME=atoi((*serverConfig)["sessionTimeout"]);
	if(MAXIDLETIME<=0)
		MAXIDLETIME=1200;
	MAXTHREADS=atoi((*serverConfig)["maxthreads"]);
	if(MAXTHREADS<=16 || MAXTHREADS>THREADLIMIT-2)
		MAXTHREADS=64;
	if(dbHome[0]==0)
		strcpy(dbHome,(*serverConfig)["dbhome"]);            
	if(dbHome[0]==0){
		printf("Caravan Data Base directory is not set -- Cannot Continue\n");
		exit(0);
	}
	strncpy(location,(*serverConfig)["location"],31);;
	strncpy(Domain,(*serverConfig)["domain"],31);;
	strncpy(templatePath,(*serverConfig)["templates"],127);
    char *tmp=(*serverConfig)["datacache"];
    int m=atoi(tmp);
	if(m<=0)
		m=64;
	setCacheSize(m);
	tmp=(*serverConfig)["checkrequestconditions"];
	if(tmp && *tmp && (stricmp(tmp,"no")==0))
		ignoreConditions=1;
	else
		ignoreConditions=0;
	tmp=(*serverConfig)["tmp"];
	if(tmp==0 || *tmp==0)   tmp=getenv("tmp");
	if(tmp)
		strcpy(tmpDirectory,tmp);
	tmp=getenv("etc");
	if(tmp)
		strcpy(etcDirectory,tmp);
	line[0]=0;
#ifndef READ_ONLY
	if(!checkDirectory(tmpDirectory)){
		printf("Environment variable \"tmp\" does not point to a valid directory, Cannot Continue\n");
	}
#endif
	printf("location=%s\n",location);
	init_bf();
	initCaravan();
	Console=new RealFile(CONSOLE);
	mkDirectory(dbHome);
	Buffer *buffer=new Buffer(1024*1024);
	initDBMS();
	delete buffer;
	startScheduler();
	if(repair){
		char *atrepair="Repair Mode: Caravan will repair all tables and exit!\n===============================================\n";
		caravanlog(atrepair);
		printf(atrepair);
		reIndex();
		printf("Repair Complete, Caravan will now quit\n");
		shutDown();
		repairDone=1;
		SleepMilSec(300000);
		exit(0);
	}else
		tcpWait();
	initSearchEngine();
//	initCodeMem();
//	int getCodeMem();
//	int getCodeBlocks();
//	extern int oval;
	initDynamo();
//	printf("compiled codesize=%d %d in %d blocks\r\n",oval,getCodeMem(),getCodeBlocks());
	//void initmemlog();
	//initmemlog();
	res_init();
	smtpd=0;
	ftpd=0;
    char* services=(*serverConfig)["services"];
	if (services && *services){
		int k=0;
		for(char* s=services;;s++){
			if(*s==';' || *s==0){
				char c=*s;
				*s=0;
				if(stricmp(services,"caravan-http")==0){
					;
				}else{
					if(stricmp(services,"smtp")==0){
						;
					}else{
						if(stricmp(services,"ftp")==0){
							;
						}else{
							generic[k]=new Server(services);
							generic[k++]->start();
						}
					}
				}
				if(c==0)
					break;
				services=s;
				*s=c;
				services++;
			}
		}
	}
	httpd = new HTTPServer();
#ifndef READ_ONLY
	smtpd=new SMTPServer();
	ftpd=startFtp();
#endif
	return 0;
}


#ifdef GUI


#include "caroptions.h"

caravanOptions* opts=0;
int restart=0;


void readOptions(Property * props)
{
        if (!serverConfig)
                return;
        if(opts==0){
                Buffer *tmp=new Buffer(sizeof(caravanOptions));
                opts=(caravanOptions*)((char*)*tmp);
                opts->thisBuffer=tmp;
        }
        memset(opts,0,sizeof(caravanOptions)-sizeof(void*));
        strcpy(opts->caravan,props->getPath());
        char* tmp=opts->caravan;
        for(int i=strlen(tmp);i>0;--i){
                if(tmp[i]==FSLASH){
                        tmp[i]=0;
                        break;
                }
        }
        if(strlen((*serverConfig)["file"])==0){
                opts->config=1;
                return;
        }
        if(strlen((*serverConfig)["dbhome"])==0){
                opts->config=2;
                return;
        }
        strcpy(opts->temp,tmpDirectory);
        strcpy(opts->dbhome,(*props)["dbhome"]);
        strcpy(opts->templates,(*props)["templates"]);
        strcpy(opts->documents,(*props)["documents"]);
        strcpy(opts->bin,(*props)["bin"]);
        strcpy(opts->location,(*props)["location"]);
        strcpy(opts->backup,(*props)["backup"]);
        Time *t=new Time((ULONG) htoi((*props)["time"]));
        strcpy(opts->time,t->local());
        delete t;
        tmp=(*props)["put"];
        if(tmp && *tmp){
                opts->putOn=1;
                strcpy(opts->put,tmp);
        }
        tmp=(*props)["smtp"];
        if(tmp && *tmp)
                opts->smtpOn=1;
        strcpy(opts->home,(*props)["home"]);
        strcpy(opts->domain,Domain);
        strcpy(opts->timeout,(*props)["sessionTimeout"]);
        if(atoi(opts->timeout)<=0)
                strcpy(opts->timeout,"20");
        else
                snprintf(opts->timeout,8,"%d",atoi(opts->timeout)/60);
        tmp=(*props)["nameserver"];
        char* s=tmp;
        while(1){
                if(*s==';' || *s==0){
                        strncpy(opts->dns1,tmp,s-tmp);
                        opts->dns1[s-tmp]=0;
                        if(*s)
                                s++;
                        break;
                }
                s++;
        }
        tmp=s;
        while(1){
                if(*s==';' || *s==0){
                        strncpy(opts->dns2,tmp,s-tmp);
                        opts->dns2[s-tmp]=0;
                        break;
                }
                s++;
        }
        tmp=(*props)["address"];
        if(tmp && *tmp){
                strcpy(opts->address,tmp);
                opts->addressOn=1; 
        }
        snprintf(opts->maxThreads,8,"%d",MAXTHREADS);
        tmp=(*props)["ftp"];
        if(tmp && *tmp){
                opts->ftpOn=1;
                strcpy(opts->ftpPort,tmp);        
        }
        tmp=(*props)["datacache"];
        if(tmp && *tmp){
                strcpy(opts->datacache,tmp);
        }else
                strcpy(opts->datacache,"1");
        tmp=(*props)["caravan-http"];
        if(tmp && *tmp){
                strcpy(opts->httpPort,tmp);
        }else
                strcpy(opts->httpPort,"80");
        tmp=(*props)[gateway];
        if(tmp && *tmp){
                opts->gatewayOn=1;
                strcpy(opts->gateway,tmp);
        }
}

static ULONG saveit=0;

void initOptions()
{
   readOptions(serverConfig);
   if(saveit){
           Time* t=new Time(saveit);
           strcpy(opts->time,t->local());
           delete t;
   }
}



int  makeSetupDirectory(char* dirname,char*msg)
{
        if(checkDirectory(dirname)==0){
                if(opts->mkDir){
                        if(mkDirectory(dirname)==0){
                                snprintf(msg+strlen(msg),64,"Error ! Could not create:%s\n",dirname);
                                return 0;
                        }else{
                                snprintf(msg+strlen(msg),64,"Created:%s\n",dirname);
                                return 1;
                        }
                }else{
                        snprintf(msg+strlen(msg),128,"Folder \"%s\" does not exist!\nPlease check Create Folders option\nor make the directory manually",dirname);
                        return 0;
                }
        }
        return 1;
}

void applyOptions(Buffer* msg,int save)
{
        int changes=0;
        char *feedBack=*msg;
        *feedBack=0;
        if(opts==0){
                return;
        }
        Buffer* mb=new Buffer(512);
        char* mkd=*mb;
        *mkd=0;
        if(stricmp(tmpDirectory,opts->temp)){
                if(makeSetupDirectory(opts->temp,mkd)==0){
                        strcpy(feedBack,mkd);
                        delete mb;
                        return ;
                }
                changes++;
                strcpy(tmpDirectory,opts->temp);
                serverConfig->modify("tmp",opts->temp);
        }
        char* tmp=(*serverConfig)["put"];
        if(opts->putOn){
                if(stricmp(tmp,opts->put)){
                        serverConfig->modify("put",opts->put);
                        changes++;
                }
        }else{
                if(tmp && *tmp){
                        serverConfig->modify("put","");
                        changes++;
                }
        }
        tmp=(*serverConfig)["home"];
        if(stricmp(tmp,opts->home)){
                serverConfig->modify("home",opts->home);
                changes++;
        }
        tmp=(*serverConfig)["documents"];
        if(stricmp(tmp,opts->documents)){
                makeSetupDirectory(opts->documents,mkd);
                serverConfig->modify("documents",opts->documents);
                changes++;
        }
        tmp=(*serverConfig)["bin"];
        if(stricmp(tmp,opts->bin)){
                makeSetupDirectory(opts->bin,mkd);
                serverConfig->modify("bin",opts->bin);
                changes++;
        }
        tmp=(*serverConfig)["domain"];
        if((tmp==0 || *tmp==0) && *opts->domain){
                serverConfig->modify("domain",opts->domain);
                changes++;
        }
        tmp=(*serverConfig)["nameserver"];
        char dns1[20];
        char dns2[20];
        dns1[0]=0;
        dns2[0]=0;
        char* s=tmp;
        while(1){
                if(*s==';' || *s==0){
                        strncpy(dns1,tmp,s-tmp);
                        dns1[s-tmp]=0;
                        if(*s)
                                s++;
                        break;
                }
                s++;
        }
        tmp=s;
        while(1){
                if(*s==';' || *s==0){
                        strncpy(dns2,tmp,s-tmp);
                        dns1[s-tmp]=0;
                        break;
                }
                s++;
        }
        Buffer *d1=checkAddress(opts->dns1);
        Buffer *d2=checkAddress(opts->dns2);      
        if((stricmp(opts->dns1,dns1)&&d1)  || (stricmp(opts->dns2,dns2) && d2)){
                char dns[128];
				char *dlim="";
				if(*opts->dns2)
					dlim=";";
				int l=snprintf(dns,128,"%s%s%s%s",opts->dns1,dlim,opts->dns2,s);
                serverConfig->modify("nameserver",dns);
                res_init();
                changes++;
        }
        delete d1;
        delete d2;
        tmp=(*serverConfig)["address"];
        int m=0;
        if(tmp && *tmp){
                if(opts->addressOn==0){
                        serverConfig->modify("address","");
                        m=1;
                }else{
                        if(strcmp(tmp,opts->address)){
                                serverConfig->modify("address",opts->address);
                                m=1;
                        }
                }
        }else{
                if(opts->addressOn){
                        serverConfig->modify("address",opts->address);
                        m=1;
                }
        }
        int port=atoi((*serverConfig)["caravan-http"]);
        if(port==0)
                port=80;
        if(atoi(opts->httpPort)!=port){
                m++;
        }
        serverConfig->modify("caravan-http",opts->httpPort);
        if(m && !opts->config){
                changes++;
                delete httpd;
                httpd = new HTTPServer();
        }
        m=0;
        tmp=(*serverConfig)["ftp"];
        if(tmp && *tmp){
                if(opts->ftpOn){
                        if(atoi(opts->ftpPort)!=atoi(tmp))               
                                m++;
                }else{
                        serverConfig->modify("ftp","");
                        m++;
                }
        }else{
                if(opts->ftpOn && atoi(opts->ftpPort)>0){
                        m++;
                }
        }
        if(m){
                changes++;
                delete ftpd;
                ftpd=0;
                if(opts->ftpOn){
                        serverConfig->modify("ftp",opts->ftpPort);
                        if(!opts->config)
                                ftpd=startFtp();
                }
        }
        tmp=(*serverConfig)["smtp"];
        m=0;
        if(tmp && *tmp){
                if(opts->smtpOn==0){
                        serverConfig->modify("smtp","");
                        m++;
                }
        }else{
                if(opts->smtpOn){
                        serverConfig->modify("smtp","25");
                        m++;
                }
        }
        if(m){
                if(opts->smtpOn && !opts->config)
                        smtpd=new SMTPServer();
                else{
                        delete smtpd;
                        smtpd=0;
                }
                changes++;
        }
        tmp=(*serverConfig)["datacache"];
        m=atoi(tmp);
        if(m==0)
                m=1;
        if(atoi(opts->datacache)!=m){
                int dc=atoi(opts->datacache);
                if((dc*1024*1024)>0){
                        setCacheSize(dc);                 
                        serverConfig->modify("datacache",opts->datacache);
                        changes++;
                }
        }
        m=atoi(opts->maxThreads);
        tmp=(*serverConfig)["maxthreads"];
        if(m!=MAXTHREADS && m!=atoi(tmp)){
                        if(m>16 && m<=256){
                                serverConfig->modify("maxthreads",opts->maxThreads);
                                MAXTHREADS=m;
                                changes++;
                        }
        }
        m=0;
        tmp=(*serverConfig)[gateway];
        if(tmp && *tmp){
                if(opts->gatewayOn){
                        if(stricmp(tmp,opts->gateway))
                                m++;
                }else{
                        m++;
                        *opts->gateway=0;
                }
        }else{
                if(opts->gatewayOn)
                        m++;
        }
        if(m){
                changes++;
                serverConfig->modify(gateway,opts->gateway);
        }
        m=atoi(opts->timeout);
        if(MAXIDLETIME!=m*60){  
                snprintf(dns1,8,"%d",MAXIDLETIME=m*60);
                serverConfig->modify("sessionTimeout",dns1);
                changes++;
        }
        m=changes;
        if(save){
                tmp=(*serverConfig)["templates"];
                if(stricmp(tmp,opts->templates)){
                        makeSetupDirectory(opts->templates,mkd);
                        serverConfig->modify("templates",opts->templates);
                        changes++;
                }
                tmp=(*serverConfig)["dbhome"];
                if(stricmp(tmp,opts->dbhome)){
                        if(makeSetupDirectory(opts->dbhome,mkd)==0){
                                strcpy(feedBack,mkd);
                                delete mb;
                                return ;
                        }
                        serverConfig->modify("dbhome",opts->dbhome);
                        changes++;
                }
                tmp=(*serverConfig)["domain"];
                if(stricmp(tmp,opts->domain)){
                        serverConfig->modify("domain",opts->domain);
                        changes++;
                }
                tmp=(*serverConfig)["location"];
                if(stricmp(tmp,opts->location)){
                        serverConfig->modify("location",opts->location);
                        changes++;
                }
                if(saveit || changes){
                        serverConfig->save(msg);
                        saveit=0;
                        strcpy(opts->backup,(*serverConfig)["backup"]);
                        Time *t=new Time((ULONG) htoi((*serverConfig)["time"]));
                        strcpy(opts->time,t->local());
                        delete t;
                        if(opts->config){
                                restart=1;
                                printf("restart all\n");
                        }
                }
        }else{
                if(m!=0){
                        Time *t=new Time();
                        strcpy(opts->time,t->local());
                        saveit=*t;
                        delete t;
                        *opts->backup=0;
                }
        }
        if(m)
                strcpy(feedBack+strlen(feedBack),"Changes were applied.\n");
        else 
                strcpy(feedBack+strlen(feedBack),"There were no changes to apply!\n");
        if(changes>m && save){
                if(!opts->config){
                        strcpy(feedBack+strlen(feedBack),"Warning !!! Some settings can be applied only after restarting caravan.\n");
                }
                else{
                        strcpy(feedBack+strlen(feedBack),"caravan is installed\nPlease open caravan.html in browser.\nBe sure to change admin password from \"password\".\n");
                        opts->config=0;
                }
        }
        m=strlen(feedBack);
        strncpy(feedBack+m,mkd,msg->getSize()-m);
        delete mb;
}


void deleteOptions()
{
        if(opts){
                Buffer* tmp=(Buffer*)opts->thisBuffer;
                delete tmp;
                opts=0;
        }
}


class starter :  public virtual ThreadSafe, public Runnable{
   void run();
   public:
           starter() {start();}
   };




void StartCaravan(int argc,char **argv)
{
	if(argc==2 && stricmp(argv[1],"repair")==0)
		repair=1;
	int i=strlen(progpath);
    for(;i>0;i--){
		if(progpath[i]==FSLASH){
			break;
		}
    }
    if(i==0)
		progpath[i++]='.';
    progpath[i]=0;
    initialize();
    new starter();
}

void PrintString(char*);


void PrintWatch(char* buff,int n)
{
	PrintString(buff);
	if(repairDone==1){
		if(n<512)
			repairDone=2;
	}
}


//int pi=0;
#ifdef TARGETLINUX

void printConsole()
{
        char buff[520];
        if(console && console->psize()){
                int n=console->read(buff,512);
                if(n>0){
                        buff[n]=0;
                        PrintWatch(buff,n);
                }
        }
}
#endif


void starter::run()
{
        SleepMilSec(500);
        if(console){
          SleepMilSec(200);
          carmain();
        }else{
                console=new Pipe();
                printf("Starting Caravan : ");
                starter *z=new starter();
                while(1){
#ifndef TARGETLINUX
                        char buff[520];
                        int n=console->read(buff,512);
                        if(n>0){
                                buff[n]=0;
                                PrintWatch(buff,n);
                        }
#else
                        SleepMilSec(500);
#endif
						if(repairDone==2){
							SleepMilSec(1000);
							exit(0);
						}
                        if(restart){
                                restart=0;
                                SleepMilSec(100);
                                carmain();
                        }
                        if(z && (z->alive()==0)){
                                delete z;
                                z=0;
                        }
                }
        }
}







void getPreviousSetup()
{
        Property *p=0;
        if(*opts->backup)
                p=new Property(opts->backup);
        else
                p=new Property();
        readOptions(p);
        strcpy(opts->domain,(*p)["domain"]);
        int m=atoi((*p)["maxthreads"]);
        if(m<=0 || m>=256)
                strcpy(opts->maxThreads,"64");
        else
           snprintf(opts->maxThreads,8,"%d",m);
        delete p;
}



#else


int main(int argc, char** argv)
{
	initialize();
	if (argc>1){
		printf("arg=%s\r\n",argv[1]);
		if (stricmp(argv[1],"repair")==0)
			repair=1;
	}
	carmain();
	//createLogo();
	SleepMilSec(1000);
	printf("Type 'CNTRL-C' to exit ..............\n");
	while(1){
		SleepMilSec(300000); 
	}
	leakprint(0);
	return 0;
}
#endif

